﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Windows.Forms;

namespace TelnetRouterApplication.Connection
{
    public class TelnetConnection : IDisposable
    {
        private static bool _DEBUG = false;
        public static bool DEBUG
        {
            get { return TelnetConnection._DEBUG; }
            set { TelnetConnection._DEBUG = value; }
        
        }
        private static int BUFFER = 131072;

        private BinaryWriter writer;
        private NetworkStream reader;
        private TcpClient client;

        private IpModel ipModel;

        private bool _connected;
        public bool connected
        {
            get { return _connected; }
            set { _connected = value; }
        }

        public TelnetConnection(IpModel ipModel)
        {
            this.ipModel = ipModel;
            connected = false;
        }

        public void Desconnect()
        {
            writer.Dispose();
            reader.Dispose();
            client.Close();
        }

        public bool Connect()
        {
            //Processo de reconexão
            if (client != null)
            {
                writer.Dispose();
                reader.Dispose();
                client.Close();
            }
            if (ipModel != null)
            {
                client = new TcpClient();
                client.Connect(ipModel.ip, ipModel.port);

                if (client.Connected)
                {
                    if (DEBUG)
                    {
                        System.Diagnostics.Debug.WriteLine("Conectado com sucesso!");
                    }
                    reader = client.GetStream();
                    
                    writer = new BinaryWriter(reader);

                    return true;
                }
            }
            return false;
        }

        public void SendMessage(string message)
        {
            if (DEBUG)
            {
                System.Diagnostics.Debug.WriteLine("Mensagem enviada: " + message + "");
            }

            byte[] sendBytes = Encoding.ASCII.GetBytes(message + "\r\n");

            try
            {
                reader.Write(sendBytes, 0, sendBytes.Length);
            }
            catch(Exception e)
            {
                MessageBox.Show("Connection Error! Check you connection!");    
            }
        }

        public void SendMessageNoEnter(string message)
        {
            if (DEBUG)
            {
                System.Diagnostics.Debug.WriteLine("Mensagem enviada: " + message+"");
            }

            byte[] sendBytes = Encoding.ASCII.GetBytes(message);
            try
            {
                reader.Write(sendBytes, 0, sendBytes.Length);
            }
            catch (Exception e)
            {
                MessageBox.Show("Connection Error! Check you connection!");    
            }
        }

        public void Delay(int milisseconds)
        {
            Thread.Sleep(milisseconds);
        }

        public List<String> ReceiveMessage()
        {
            return ReceiveMessage(true);
        }

        //Essa variável booleana é para dividir ou não a mensagem pelos \r
        public List<String> ReceiveMessage(bool split)
        {
            //Força sempre um delay de 250ms para garantir o recebimento das mensagens
            Delay(250);

            ASCIIEncoding encoder = new ASCIIEncoding();
            List<String> messages = new List<String>();
            string messageTotal = "";
            byte[] message = new byte[BUFFER];
            int bytesRead;
            bool reading = true;

            string messageReceived = "";
            while (reading)
            {
                bytesRead = 0;

                try
                {
                    if (reader.DataAvailable)
                    {
                        //blocks until a client sends a message
                        bytesRead = reader.Read(message, 0, BUFFER);
                        if (DEBUG)
                        {
                            messageReceived = encoder.GetString(message, 0, bytesRead);
                        }
                    }
                    else
                    {
                        reading = false;
                    }
                }
                catch
                {
                    //a socket error has occured
                    reading = false;
                    break;
                }

                if (bytesRead == 0)
                {
                    //the client has disconnected from the server
                    reading = false;
                    break;
                }
                
                messageReceived = encoder.GetString(message, 0, bytesRead);

                messageTotal += messageReceived;
            }
            if (split)
            {
                messageTotal.Replace("\n", "");
                messages = new List<string>(messageTotal.Split('\r'));
                if (DEBUG)
                {
                    System.Diagnostics.Debug.WriteLine("Mensagem recebida (com split): ");
                    int i = 0;
                    foreach (String messageIndex in messages)
                    {
                        System.Diagnostics.Debug.WriteLine(i + " >" + messageIndex.Replace('\n', ' '));
                        i++;
                    }
                }
            }
            else
            {
                messages.Add(messageTotal);
                if (DEBUG)
                {
                    System.Diagnostics.Debug.WriteLine("Mensagem recebida (sem split): ");
                    System.Diagnostics.Debug.WriteLine(messageTotal);
                }
            }
            return messages;
        }

        
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            //Essa bool é do padrão de projetos da microsoft que indica que quando você passa um
            //true para o disposing, você deve limpar e liberar os recursos da entidade
            if (disposing)
            {
                reader.Dispose();
                writer.Dispose();
                client.Close();
            }
        }
    }
}
